//
// Created by yuan on 25-8-12.
//

#ifndef STREAM_ENCODER_H
#define STREAM_ENCODER_H

#include "otl_ffmpeg.h"
#include "otl_log.h"
#include <memory>
#include <string>
#include <atomic>
#include <cstdint>
#include <vector>

namespace enrigin {
    // Encoding parameters (common fields; extend via options)
    struct EncodeParam {
        std::string codecName{"h264"};
        int width{0};
        int height{0};
        AVRational timeBase{1, 90000};
        AVRational frameRate{0, 1};    // optional, 0 means unknown
        AVPixelFormat pixFmt{AV_PIX_FMT_YUV420P};
        int64_t bitRate{0};            // bps, 0 means use crf/qp
        int gopSize{0};                // 0 means encoder default
        int maxBFrames{-1};            // <0 means encoder default
        int threadCount{0};            // 0 means auto

        // Rate control
        int crf{-1};                   // supported by libx264/libx265
        int qp{-1};                    // constant QP
        std::string preset;            // veryfast, medium, slow, ...
        std::string tune;              // zerolatency, film, ...
        std::string profile;           // high, main, baseline, ...

        // Extra key-value pairs, passed to priv_data
        AVDictionary* options{nullptr}; // caller owns the lifetime

        // Hardware encoding preferences
        bool preferHardware{true};       // prefer hardware encoder when available
        std::string hwAccel;             // preferred backend: videotoolbox/nvenc/qsv/vaapi/amf (empty = auto)

        // VSV hardware configuration (optional): if cardId/vpuId or hwPixelFormat is set, initialize HW device/frames
        int cardId{-1};
        int vpuId{-1};
        AVPixelFormat hwPixelFormat{AV_PIX_FMT_NONE}; // e.g. AV_PIX_FMT_VSV

        // Adaptation strategy
        bool autoUploadHw{true};               // if hw_frames_ctx exists, auto SW->HW upload
        bool allowSwScale{false};              // allow internal swscale for size/format adaptation
        AVPixelFormat preferredSwPixFmt{AV_PIX_FMT_NV12}; // preferred SW input format to minimize conversion
        AVPixelFormat preferredSwInput{AV_PIX_FMT_NV12};  // legacy name used in implementation

        // External HW frames context (e.g., from decoder) for true zero-copy
        // If set, encoder will adopt this hw_frames_ctx and its associated device
        AVBufferRef* externalHwFramesCtx{nullptr};
    };

    // Encoder input specification (caller may decide upstream scaling based on this)
    struct InputSpec {
        int width{0};
        int height{0};
        bool acceptsSw{true};
        bool acceptsHw{false};
        AVPixelFormat preferredSwPixFmt{AV_PIX_FMT_NV12};
        AVPixelFormat preferredHwPixFmt{AV_PIX_FMT_NONE};
    };

    class StreamEncoder {
    public:
        StreamEncoder();
        virtual ~StreamEncoder();
        virtual int init(EncodeParam *params) = 0;
        // Legacy encode API (compatibility)
        virtual int encode(AVFrame* frame, AVPacket **p_pkt, int *p_num) = 0;

        // New: vector-based API. Default returns -1 to indicate not implemented by encoder.
        // Encoder should allocate AVPacket* elements; caller must free each via freePacket().
        virtual int encode(AVFrame* frame, std::vector<AVPacket*>& outPkts) { (void)frame; (void)outPkts; return -1; }

        // Free a single packet allocated/owned by encoder. Default uses libav to free.
        virtual void freePacket(AVPacket* pkt) { if (pkt) { av_packet_unref(pkt); av_packet_free(&pkt); } }

        // Provide output stream parameters for muxer setup (used in re-encode)
        // Defaults are safe fallbacks; encoders may override.
        virtual const AVCodecParameters* getCodecParameters() const { return nullptr; }
        virtual AVRational getTimeBase() const { return {1, 90000}; }

        // Query encoder expected input specification
        virtual int getInputSpec(InputSpec &spec) const { (void)spec; return -1; }

        // Request the next frame to be encoded as an IDR keyframe
        virtual int requestKeyFrame() = 0;

        // Get encoder FPS statistics
        // Returns: average fps; frames returns accumulated frames; elapsedSec returns duration in seconds
        virtual double getFps(uint64_t &frames, double &elapsedSec) const = 0;
    };

    // Factory: create encoder by codec name (h264/h265/hevc/mjpeg/mpeg4/libx264/libx265/...)
    std::unique_ptr<StreamEncoder> CreateStreamEncoder(const std::string &codecName);
}




#endif //STREAM_ENCODER_H
